package com.neusoft.bizcore.web;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.neusoft.bizcore.auth.common.jwt.JwtAuthProvider;
import com.neusoft.bizcore.web.config.BizcoreWebProperties;
import com.neusoft.bizcore.web.filter.LogFilter;
import com.neusoft.bizcore.web.filter.XssSqlFilter;
import com.neusoft.bizcore.web.support.CurrentUserArgumentResolver;
import com.neusoft.bizcore.web.support.SearchableArgumentResolver;

import lombok.extern.slf4j.Slf4j;

/**
 * descriptionddf
 *
 * @author sunchf
 * @date 2018年12月6日 下午2:06:02
 */

@Slf4j
@Configuration
@EnableConfigurationProperties(BizcoreWebProperties.class)
@ComponentScan
public class BizcoreWebConfiguration implements WebMvcConfigurer, ServletContextInitializer {

    @Autowired
    private Environment env;

    @Autowired
    private BizcoreWebProperties properties;

    @Bean
    @ConditionalOnMissingBean(JwtAuthProvider.class)
    public JwtAuthProvider jwtAuthProvider() {
        return new JwtAuthProvider(this.properties.getJwt().getJwtExpiration(),
                this.properties.getJwt().getJwtSigningKey());
    }

    /**
     * CORS filter
     *
     * @return corsFilter
     */
    @Bean
    @ConditionalOnProperty(name = "bizcore.web.cors.allowed-origins")
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = this.properties.getCors();
        final List<String> paths = this.properties.getCorsPath();
        for (final String path : paths) {
            source.registerCorsConfiguration(path, config);
        }
        return new CorsFilter(source);
    }

    @Bean
    public FilterRegistrationBean<XssSqlFilter> xssFilterRegistrationBean() {
        final FilterRegistrationBean<XssSqlFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new XssSqlFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.setEnabled(this.properties.getXss().isEnabled());
        filterRegistrationBean.addUrlPatterns("/*");
        final Map<String, String> initParameters = new HashMap<>();
        initParameters.put("excludes", this.properties.getXss().getExcludes());
        filterRegistrationBean.setInitParameters(initParameters);
        return filterRegistrationBean;
    }

    @Bean
    @ConditionalOnProperty(name = "bizcore.web.restLoggingEnabled", havingValue = "true")
    public FilterRegistrationBean<LogFilter> logFilterRegistrationBean() {
        final FilterRegistrationBean<LogFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

    @Bean
    public RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

    @Bean
    public SearchableArgumentResolver searchableArgumentResolver() {
        return new SearchableArgumentResolver();
    }

    @Bean
    public PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver() {
        final PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
        resolver.setMaxPageSize(this.properties.getPageable().getMaxPageSize());
        resolver.setPageParameterName(this.properties.getPageable().getPageParameterName());
        resolver.setSizeParameterName(this.properties.getPageable().getSizeParameterName());
        resolver.setPrefix(this.properties.getPageable().getPrefix());
        resolver.setQualifierDelimiter(this.properties.getPageable().getQualifierDelimiter());
        return resolver;
    }

    @Bean
    public CurrentUserArgumentResolver currentUserArgumentResolver() {
        return new CurrentUserArgumentResolver(this.jwtAuthProvider());
    }

    @Override
    public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(this.pageableHandlerMethodArgumentResolver());
        argumentResolvers.add(this.searchableArgumentResolver());
        argumentResolvers.add(this.currentUserArgumentResolver());
    }

    @Override
    public void onStartup(final ServletContext servletContext) throws ServletException {
        if (this.env.getActiveProfiles().length != 0) {
            if (BizcoreWebConfiguration.log.isInfoEnabled()) {
                BizcoreWebConfiguration.log.info("Web application configuration, using profiles: {}",
                        Arrays.toString(this.env.getActiveProfiles()));
            }
        }

        if (BizcoreWebConfiguration.log.isInfoEnabled()) {
            BizcoreWebConfiguration.log.info("Web application fully configured");
        }
    }

}
